package jdesktop.appbar.win32;

import com.sun.jna.Pointer;
import jdesktop.appbar.AppBarModel;

import java.awt.*;
import java.util.logging.Level;

import static jdesktop.appbar.win32.Shell32.*;

/**
 * Subclasses the existing window to add these features:
 * <ul>
 * <li>handling the appbar notification message from the Shell and forwarding to the
 * {@link AppBarEventHandler} instance.</li>
 * <li>handling the {@linkplain #handleHitTest(com.sun.jna.Pointer, int, int, int)} hittest} on the non-client area to make sizing handles not on the leading
 * edge of the appbar.</li>
 * <li>Responding to size, position and activation notifications</li>
 * </ul>
 */
public class DockedWndProc extends CommonWndProc
{
    /**
     * Receiver for appbar events.
     */
    protected AppBarEventHandler notify;

    /**
     * A unique system-wide message. This is sent by the Shell to the appbar window to notify
     * it of appbar events.
     */
    static public final int notifyAppBarMsg = user32.RegisterWindowMessageA("JNA_AppBarWndProc_Notification");


    private int hbrBackground = GDI32.INSTANCE.CreateSolidBrush(6);

    public DockedWndProc(WndProcState state, AppBarEventHandler notify)
    {
        super(state);
        this.notify = notify;
    }

    /**
     * The wndProc function for the window subclass.
     *
     * @param hWnd
     * @param uMsg
     * @param wParam
     * @param lParam
     * @return
     */
    protected void handleMessage(Pointer hWnd, int uMsg, int wParam, int lParam)
    {
        assert windowProcedureState.getAppBarModel().isDocked();
        switch (uMsg)
        {
            default:
                if (uMsg == notifyAppBarMsg)
                    setMessageResult(handleAppBarMessage(wParam, lParam));
        }
        super.handleMessage(hWnd, uMsg, wParam, lParam);
    }

    protected void handleGetMinMaxInfo(int pMinMaxInfo)
    {
        getMinMaxInfo(pMinMaxInfo);
    }

    protected void handleEraseBackground(Pointer hWnd, int hDC)
    {
        if (isDocked() && hbrBackground!=-1)
        {
            user32.GetClientRect(hWnd, scratchRect);
            user32.FillRect(hDC, scratchRect,hbrBackground);
            setMessageResult(1);
        }
    }

    protected void handleMove()
    {
        //boundsChanged();
    }

    protected void handleSize()
    {
        boundsChanged();
    }

    protected void boundsChanged()
    {
        boolean update = ((windowProcedureState.isDragAppBarWidthWindow() || !windowProcedureState.isMovingOrSizing()));
        if (log.isLoggable(Level.FINER))
            log.log(Level.FINER, "window bounds changed, updating:"+update);
        if (update)
            messageHandler.onBoundsChanged();
    }

    @Override
    protected Rectangle appBarSizing(int wParam, int left, int top, int right, int bottom)
    {
        return messageHandler.appBarSizing(wParam, left, top, right, bottom);
    }

    /**
     * Handles notifications from the Shell to the appbar.
     * @param wParam
     * @param lParam
     * @return
     */
    private int handleAppBarMessage(int wParam, int lParam)
    {
        AppBarModel target = windowProcedureState.getAppBarModel();
        switch (wParam)
        {
            // notification that the size may have changed
            case Shell32.ABN_POSCHANGED:
                notify.appBarPositionChanged(target);
                break;

            case Shell32.ABN_STATECHANGE:
                notify.appBarStateChange(target);
                break;

            case Shell32.ABN_FULLSCREENAPP:
                boolean opening = lParam != 0;
                notify.appBarFullScreenApp(target,opening);
                break;

            case Shell32.ABN_WINDOWARRANGE:
                boolean begin = lParam != 0;
                notify.appBarWindowArrange(target,begin);
        }
        return 0;
    }


    public boolean isDocked()
    {
        return windowProcedureState.getAppBarModel().isDocked();
    }
}
